 #include "CMediaFile.h"
#include "GenOpt.h"
#include <QFile>
#include <QStringList>
#include <QTextStream>
#include <chrono>
#include <cstdint>
#include <string>
#include <sysinfoapi.h>

CMediaFile::CMediaFile() {
  av_fmt_ctx_ = nullptr;
  av_codec_ctx_ = nullptr;
  frame_codec_ = nullptr;
  frame_yuv_ = nullptr;
  av_packet_ = nullptr;
  output_buf_ = nullptr;
  av_conv_ctx_ = nullptr;
  video_index_ = 0;
  display_ = false;
  thread_watch_ = false;
  frame_cb_func_ = nullptr;
  frame_cb_data_ = nullptr;
  fps_ctrl_ms_ = 0;
  stop_ = true;
  state_ = AVM_STATE_CLOSE;
  thread_codec_ = {0};
  seeking_ = false;
  avformat_network_init();
  av_log_set_level(AV_LOG_ERROR);
};

static double r2d(AVRational r) {
  return (r.den == 0) ? 0 : ((double)r.num / (double)r.den);
}

CMediaFile::~CMediaFile() { avformat_network_deinit(); }

int CMediaFile::Open(std::string url, HWND hWnd) {
  int ret = 0;
  const AVCodec *codec;
  av_fmt_ctx_ = avformat_alloc_context();
  AVDictionary *opts = NULL;
  av_dict_set(&opts, "timeout", "3000", 0);
  if (avformat_open_input(&av_fmt_ctx_, url.c_str(), nullptr, &opts) != 0) {
    return AVM_FAILED;
  }
  if (avformat_find_stream_info(av_fmt_ctx_, NULL) < 0) {
    Close();
    return AVM_FAILED;
  }

  video_index_ = -1;
  for (unsigned int i = 0; i < av_fmt_ctx_->nb_streams; i++) {
    if (av_fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
      video_index_ = i;
      break;
    }
  }

  if (video_index_ == -1) {
    Close();
    return AVM_NOT_FOUND;
  }

  av_codec_ctx_ = avcodec_alloc_context3(NULL);
  ret = avcodec_parameters_to_context(
      av_codec_ctx_, av_fmt_ctx_->streams[video_index_]->codecpar);

  codec = avcodec_find_decoder(av_codec_ctx_->codec_id);
  if (codec == NULL) {
    Close();
    return AVM_NO_DEOCDER;
  }

  if (avcodec_open2(av_codec_ctx_, codec, NULL) < 0) {
    Close();
    return AVM_FAILED;
  }

  frame_codec_ = av_frame_alloc();
  frame_yuv_ = av_frame_alloc();

  int buf_size = av_image_get_buffer_size(
      AV_PIX_FMT_YUV420P, av_codec_ctx_->width, av_codec_ctx_->height, 1);
  output_buf_ = (uint8_t *)av_malloc(buf_size);
  av_image_fill_arrays(frame_yuv_->data, frame_yuv_->linesize, output_buf_,
                       AV_PIX_FMT_YUV420P, av_codec_ctx_->width,
                       av_codec_ctx_->height, 1);
  frame_yuv_->format = AV_PIX_FMT_YUV420P;
  av_conv_ctx_ = sws_getCachedContext(
      av_conv_ctx_, av_codec_ctx_->width, av_codec_ctx_->height,
      av_codec_ctx_->pix_fmt, av_codec_ctx_->width, av_codec_ctx_->height,
      AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  av_packet_ = av_packet_alloc();
  fps_ctrl_ms_ =
      (uint64_t)1000 /
      ((float)av_fmt_ctx_->streams[video_index_]->avg_frame_rate.num /
       av_fmt_ctx_->streams[video_index_]->avg_frame_rate.den);
  state_ = AVM_STATE_OPEN;
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  thread_watch_ = true;
  pthread_create(&thread_codec_, &attr, CMediaFile::thread_codec_func, this);
  return 0;
}

int CMediaFile::GetInfo(AVMMediaInfo_t *info) {
  if (state_ < AVM_STATE_OPEN) {
    return AVM_NOT_OPEN;
  }
  info->stream_cnt = av_fmt_ctx_->nb_streams;
  info->bit_rate = av_fmt_ctx_->bit_rate;
  info->duration = av_fmt_ctx_->duration;
  info->fps = (float)av_fmt_ctx_->streams[video_index_]->avg_frame_rate.num /
              av_fmt_ctx_->streams[video_index_]->avg_frame_rate.den;
  strncpy_s(info->fmt, av_fmt_ctx_->iformat->name, 64);
  info->height = av_codec_ctx_->height;
  info->width = av_codec_ctx_->width;
  if (av_fmt_ctx_->streams[video_index_]->nb_frames != 0) {
    info->total_frames = av_fmt_ctx_->streams[video_index_]->nb_frames;
  } else {
    info->total_frames = info->fps * info->duration / 1000000;
  }

  info->video_index = video_index_;
  return AVM_SUCCESS;
}

int CMediaFile::Close() {
  state_ = AVM_STATE_CLOSE;
  thread_watch_ = false;
  pthread_join(thread_codec_, NULL);
  if (output_buf_) {
    av_free(output_buf_);
    output_buf_ = NULL;
  }
  if (av_conv_ctx_) {
    sws_freeContext(av_conv_ctx_);
    av_conv_ctx_ = nullptr;
  }
  if (av_packet_) {
    av_packet_unref(av_packet_);
    av_packet_free(&av_packet_);
    av_packet_ = nullptr;
  }
  if (frame_codec_) {
    av_frame_unref(frame_codec_);
    av_frame_free(&frame_codec_);
    frame_codec_ = nullptr;
  }
  if (frame_yuv_) {
    av_frame_unref(frame_yuv_);
    av_frame_free(&frame_yuv_);
    frame_yuv_ = nullptr;
  }
  if (av_codec_ctx_) {
    avcodec_close(av_codec_ctx_);
    av_codec_ctx_ = nullptr;
  }
  if (av_fmt_ctx_) {
    avformat_close_input(&av_fmt_ctx_);
    av_fmt_ctx_ = nullptr;
  }
  return 0;
}

int CMediaFile::SetFrameValidCallBackFunc(FrameCb cb, void *userdata) {
  if (cb) {
    frame_cb_func_ = cb;
  }
  if (userdata) {
    frame_cb_data_ = userdata;
  }
  return AVM_SUCCESS;
}

int CMediaFile::Play() {
  if (state_ < AVM_STATE_OPEN) {
    return AVM_NOT_OPEN;
  }
  state_ = AVM_STATE_START;
  return AVM_SUCCESS;
}

int CMediaFile::Pause() {
  if (state_ < AVM_STATE_OPEN) {
    return AVM_NOT_OPEN;
  }
  state_ = AVM_STATE_PAUSE;
  return AVM_SUCCESS;
}

void *CMediaFile::thread_codec_func(void *param) {
  CMediaFile *p = (CMediaFile *)param;
  int ret = 0;
  uint64_t time_s = 0;
  uint64_t time_n = 0;
  AVMFrame_t frame = {0};
  bool frame_init = false;
  while (p->thread_watch_) {
    time_s = GetTickCount64();
    if (!p->mutex_lock_.try_lock()) {
      Sleep(100);
      continue;
    }
    if ((p->state_ == AVM_STATE_PAUSE) && (p->frame_cb_func_) && frame_init) {
      frame.state = AVM_STATE_PAUSE;
      p->frame_cb_func_(&frame, p->frame_cb_data_);
      p->mutex_lock_.unlock();
      Sleep(100);
      continue;
    }

    float fps =
        (float)p->av_fmt_ctx_->streams[p->video_index_]->avg_frame_rate.num /
        p->av_fmt_ctx_->streams[p->video_index_]->avg_frame_rate.den;
    ret = av_read_frame(p->av_fmt_ctx_, p->av_packet_);
    /*play video from start point while finish*/
    if (ret < 0) {
      av_seek_frame(p->av_fmt_ctx_, -1, 0,
                    AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
      av_packet_unref(p->av_packet_);
      p->mutex_lock_.unlock();
      Sleep(1000);
      continue;
    }

    if (p->av_packet_->stream_index != p->video_index_) {
      av_packet_unref(p->av_packet_);
      p->mutex_lock_.unlock();
      continue;
    }

    ret = avcodec_send_packet(p->av_codec_ctx_, p->av_packet_);
    if (ret != 0) {
      av_packet_unref(p->av_packet_);
      p->mutex_lock_.unlock();
      continue;
    }

    ret = avcodec_receive_frame(p->av_codec_ctx_, p->frame_codec_);
    if (ret < 0) {
      av_packet_unref(p->av_packet_);
      p->mutex_lock_.unlock();
      continue;
    }
    sws_scale(p->av_conv_ctx_, (const uint8_t *const *)p->frame_codec_->data,
              p->frame_codec_->linesize, 0, p->frame_codec_->height,
              p->frame_yuv_->data, p->frame_yuv_->linesize);
    if (p->frame_cb_func_) {
      frame.data[0] = p->frame_yuv_->data[0];
      frame.data[1] = p->frame_yuv_->data[1];
      frame.data[2] = p->frame_yuv_->data[2];
      frame.data_bytes[0] =
          (size_t)(p->frame_codec_->width * p->frame_codec_->height);
      frame.data_bytes[1] =
          (size_t)(p->frame_codec_->width * p->frame_codec_->height >> 2);
      frame.data_bytes[2] =
          (size_t)(p->frame_codec_->width * p->frame_codec_->height >> 2);
      frame.width = p->frame_codec_->width;
      frame.height = p->frame_codec_->height;
      frame.pts = p->frame_yuv_->pts;
      frame.stride[0] = p->frame_yuv_->linesize[0];
      frame.stride[1] = p->frame_yuv_->linesize[1];
      frame.stride[2] = p->frame_yuv_->linesize[2];
      frame.size = av_image_get_buffer_size(
          (enum AVPixelFormat)p->frame_yuv_->format, p->frame_codec_->width,
          p->frame_codec_->height, 1);
      frame.state = AVM_STATE_START;
      frame.index = p->av_packet_->pts * fps *
                    av_q2d(p->av_fmt_ctx_->streams[p->video_index_]->time_base);

      frame_init = true;
      p->frame_cb_func_(&frame, p->frame_cb_data_);
    }
    av_packet_unref(p->av_packet_);
    p->mutex_lock_.unlock();
    time_n = GetTickCount64();
    int t = (int)p->fps_ctrl_ms_ - (time_n - time_s);
    if (t > 0) {
      Sleep(t);
    }
  }
  return 0;
}

int CMediaFile::SeekByTime(uint64_t dst_time) {
  if (state_ < AVM_STATE_OPEN) {
    return AVM_NOT_OPEN;
  }
  int64_t pts =
      (int64_t)((float)dst_time /
                av_q2d(av_fmt_ctx_->streams[video_index_]->time_base));
  pts /= (1000 * 1000);
  return AVM_SUCCESS;
}

int CMediaFile::SeekByFrame(uint64_t dst_frame) {
  int ret = 0;
  int frame_idx = -1;
  if ((state_ < AVM_STATE_OPEN) || (seeking_)) {
    return AVM_NOT_OPEN;
  }
  seeking_ = true;
  mutex_lock_.lock();
  uint64_t time = dst_frame * 1000 /
                  av_q2d(av_fmt_ctx_->streams[video_index_]->avg_frame_rate);
  uint64_t seek_frame_index = time * AV_TIME_BASE / 1000;
  av_seek_frame(av_fmt_ctx_, -1, seek_frame_index,
                AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
  float fps = (float)av_fmt_ctx_->streams[video_index_]->avg_frame_rate.num /
              av_fmt_ctx_->streams[video_index_]->avg_frame_rate.den;
  do {
    ret = av_read_frame(av_fmt_ctx_, av_packet_);
    if (ret < 0) {
      av_packet_unref(av_packet_);
      seeking_ = false;
      mutex_lock_.unlock();
      return AVM_SUCCESS;
    }
    if (av_packet_->stream_index != video_index_) {
      av_packet_unref(av_packet_);
      continue;
    }
    ret = avcodec_send_packet(av_codec_ctx_, av_packet_);
    if (ret < 0) {
      av_packet_unref(av_packet_);
      continue;
    }
    ret = avcodec_receive_frame(av_codec_ctx_, frame_codec_);
    if (ret < 0) {
      av_packet_unref(av_packet_);
      continue;
    }
    frame_idx = av_packet_->pts * fps *
                av_q2d(av_fmt_ctx_->streams[video_index_]->time_base);
    if ((uint64_t)frame_idx > dst_frame + 2) {
      break;
    }
  } while (frame_idx != dst_frame);
  if (ret != 0) {
    av_packet_unref(av_packet_);
    seeking_ = false;
    mutex_lock_.unlock();
    return -1;
  }

  if (frame_cb_func_) {
    ret = sws_scale(av_conv_ctx_, (const uint8_t *const *)frame_codec_->data,
                    frame_codec_->linesize, 0, frame_codec_->height,
                    frame_yuv_->data, frame_yuv_->linesize);
    AVMFrame_t frame = {0};
    frame.data[0] = frame_yuv_->data[0];
    frame.data[1] = frame_yuv_->data[1];
    frame.data[2] = frame_yuv_->data[2];
    frame.data_bytes[0] = (size_t)(frame_codec_->width * frame_codec_->height);
    frame.data_bytes[1] =
        (size_t)(frame_codec_->width * frame_codec_->height >> 2);
    frame.data_bytes[2] =
        (size_t)(frame_codec_->width * frame_codec_->height >> 2);
    frame.width = frame_codec_->width;
    frame.height = frame_codec_->height;
    frame.pts = frame_yuv_->pts;
    frame.stride[0] = frame_yuv_->linesize[0];
    frame.stride[1] = frame_yuv_->linesize[1];
    frame.stride[2] = frame_yuv_->linesize[2];
    frame.size =
        av_image_get_buffer_size((enum AVPixelFormat)frame_yuv_->format,
                                 frame_codec_->width, frame_codec_->height, 1);
    frame.state = AVM_STATE_START;
    frame.index = frame_idx;
    frame_cb_func_(&frame, frame_cb_data_);
  }
  mutex_lock_.unlock();
  seeking_ = false;
  return AVM_SUCCESS;
}

int CMediaFile::Crop(std::string file, int type, int start, int length,
                     int interval, CropProgress pFunc, CMediaFile *avm,
                     void *usr) {
  char chindex[32];
  int frame_count;
  int name_index = 0;
  int interval_count;
  uint64_t act_pts = 0;
  EncCtx enc_ctx = {0};
  if (state_ < AVM_STATE_OPEN) {
    return AVM_NOT_OPEN;
  }
  int ret = 0;
  SeekByFrame(start);
  stop_ = false;
  frame_count = 0;
  while ((!stop_) && (frame_count < length)) {
    ret = av_read_frame(avm->av_fmt_ctx_, avm->av_packet_);
    if (avm->av_packet_->stream_index == avm->video_index_) {
      ret = avcodec_send_packet(avm->av_codec_ctx_, avm->av_packet_);
      if (ret != 0) {
        av_packet_unref(avm->av_packet_);
        continue;
      }
    }
    ret = avcodec_receive_frame(avm->av_codec_ctx_, avm->frame_codec_);
    if (ret < 0) {
      av_packet_unref(avm->av_packet_);
      continue;
    }
    if (frame_count % interval != 0) {
      frame_count++;
      if (pFunc) {
        pFunc(usr, frame_count, length);
      }
      av_packet_unref(avm->av_packet_);
      continue;
    }
    frame_count++;
    if (pFunc) {
      pFunc(usr, frame_count, length);
    }

    sws_scale(avm->av_conv_ctx_,
              (const uint8_t *const *)avm->frame_codec_->data,
              avm->frame_codec_->linesize, 0, avm->frame_codec_->height,
              avm->frame_yuv_->data, avm->frame_yuv_->linesize);
    std::string file_name;
    if (type == CROP_TYPE_JPEG) {
      sprintf(chindex, "/%d.jpg", name_index);
      file_name = file + chindex;
    } else {
      sprintf(chindex, "/video.mp4");
      file_name = file + chindex;
    }
    name_index++;
    ret = InitOutputCtx(&enc_ctx, file_name, type, av_codec_ctx_->width,
                        av_codec_ctx_->height);
    if (ret != 0) {
      av_packet_unref(avm->av_packet_);
      break;
    }
    act_pts += 40000;
    avm->frame_codec_->pts = act_pts;
    InsertFrames(file + chindex, avm->frame_codec_, &enc_ctx);
    if (type == CROP_TYPE_JPEG) {
      DestroyOutputCtx(&enc_ctx);
    }
    av_packet_unref(avm->av_packet_);
  }
  if (pFunc) {
    pFunc(usr, length, length);
  }
  if (type != CROP_TYPE_JPEG) {
    DestroyOutputCtx(&enc_ctx);
  }
  return 0;
}

int CMediaFile::StopCrop() {
  stop_ = true;
  return 0;
}

int CMediaFile::InitJpegOutputCtx(EncCtx *ctx, std::string file, int width,
                                  int height) {
  ctx->fmt_ctx = avformat_alloc_context();
  ctx->fmt_ctx->oformat = av_guess_format("mjpeg", NULL, NULL);

  if (avio_open(&ctx->fmt_ctx->pb, file.c_str(), AVIO_FLAG_READ_WRITE) < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  ctx->stream = avformat_new_stream(ctx->fmt_ctx, 0);
  if (ctx->stream == NULL) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  AVCodecParameters *paramters = ctx->stream->codecpar;
  paramters->codec_id = ctx->fmt_ctx->oformat->video_codec;
  paramters->codec_type = AVMEDIA_TYPE_VIDEO;
  paramters->format = AV_PIX_FMT_YUVJ420P;
  paramters->width = width;
  paramters->height = height;

  ctx->codec = avcodec_find_encoder(ctx->stream->codecpar->codec_id);
  if (!ctx->codec) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  ctx->codec_ctx = avcodec_alloc_context3(ctx->codec);
  if (!ctx->codec_ctx) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  if ((avcodec_parameters_to_context(ctx->codec_ctx, ctx->stream->codecpar)) <
      0) {
    DestroyOutputCtx(ctx);
    return -1;
  }
  AVRational ration = {1, 25};
  memcpy(&ctx->codec_ctx->time_base, &ration, sizeof(AVRational));
  int ret = avcodec_open2(ctx->codec_ctx, ctx->codec, NULL);
  if (ret < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  ret = avformat_write_header(ctx->fmt_ctx, NULL);
  if (ret < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }
  int y_size = width * height;
  ctx->pkt = av_packet_alloc();
  av_new_packet(ctx->pkt, 3 * y_size);
  ctx->is_write_file = true;
  return 0;
}

int CMediaFile::InitVideoOutputCtx(EncCtx *ctx, std::string file, int width,
                                   int height) {
  int ret = 0;
  if (ctx->is_init) {
    return 0;
  }
  ctx->is_init = true;
  const AVOutputFormat *outFmt = NULL;

  ret = avformat_alloc_output_context2(&ctx->fmt_ctx, NULL, NULL, file.c_str());
  if (ret < 0) {
    return -1;
  }
  outFmt = ctx->fmt_ctx->oformat;

  ret = avio_open(&ctx->fmt_ctx->pb, file.c_str(), AVIO_FLAG_READ_WRITE);
  if (ret < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  ctx->stream = avformat_new_stream(ctx->fmt_ctx, ctx->codec);
  if (ctx->stream == NULL) {
    DestroyOutputCtx(ctx);
    return -1;
  }
  ctx->stream->time_base.den = 25;
  ctx->stream->time_base.num = 1;

  AVCodecParameters *codecPara =
      ctx->fmt_ctx->streams[ctx->stream->index]->codecpar;
  codecPara->codec_id = ctx->fmt_ctx->oformat->video_codec;
  codecPara->codec_type = AVMEDIA_TYPE_VIDEO;
  codecPara->format = AV_PIX_FMT_YUV420P;
  codecPara->width = width;
  codecPara->height = height;

  ctx->codec = avcodec_find_encoder(outFmt->video_codec);
  if (ctx->codec == NULL) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  ctx->codec_ctx = avcodec_alloc_context3(ctx->codec);
  ret = avcodec_parameters_to_context(
      ctx->codec_ctx, ctx->fmt_ctx->streams[ctx->stream->index]->codecpar);
  if (ret < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }

  ctx->codec_ctx->codec_id = outFmt->video_codec;
  ctx->codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
  ctx->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
  ctx->codec_ctx->width = width;
  ctx->codec_ctx->height = height;
  ctx->codec_ctx->time_base.num = 1;
  ctx->codec_ctx->time_base.den = 25;
  ctx->codec_ctx->bit_rate = 10000000;
  ctx->codec_ctx->gop_size = 12;

  if (ctx->codec_ctx->codec_id == AV_CODEC_ID_H264) {
    ctx->codec_ctx->qmin = 10;
    ctx->codec_ctx->qmax = 51;
    ctx->codec_ctx->qcompress = (float)0.6;
  }
  if (ctx->codec_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
    ctx->codec_ctx->max_b_frames = 2;
  if (ctx->codec_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
    ctx->codec_ctx->mb_decision = 2;

  if (avcodec_open2(ctx->codec_ctx, ctx->codec, NULL) < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }
  avcodec_parameters_from_context(ctx->stream[0].codecpar, ctx->codec_ctx);
  int y_size = width * height;
  ctx->pkt = av_packet_alloc();
  av_new_packet(ctx->pkt, 3 * y_size);
  ret = avformat_write_header(ctx->fmt_ctx, NULL);
  if (ret < 0) {
    DestroyOutputCtx(ctx);
    return -1;
  }
  ctx->is_write_file = true;
  return 0;
}

int CMediaFile::InitOutputCtx(EncCtx *ctx, std::string file, int type,
                              int width, int height) {
  int ret = 0;
  ctx->enc_type = type;
  if (type == CROP_TYPE_JPEG) {
    ret = InitJpegOutputCtx(ctx, file, width, height);
  } else {
    ret = InitVideoOutputCtx(ctx, file, width, height);
  }
  return ret;
}

int CMediaFile::DestroyOutputCtx(EncCtx *ctx) {
  if (ctx->is_write_file) {
    av_write_trailer(ctx->fmt_ctx);
  }
  if (ctx->pkt) {
    av_packet_free(&ctx->pkt);
    ctx->pkt = nullptr;
  }
  if (ctx->codec_ctx) {
    avcodec_close(ctx->codec_ctx);
    ctx->codec_ctx = nullptr;
  }
  if (ctx->fmt_ctx) {
    if (ctx->fmt_ctx->pb) {
      avio_close(ctx->fmt_ctx->pb);
    }
    avformat_free_context(ctx->fmt_ctx);
    ctx->fmt_ctx = nullptr;
  }
  return 0;
}

int CMediaFile::InsertFrames(std::string file, AVFrame *pFrame, EncCtx *ctx) {
  int ret = 0;
  if (!ctx->is_write_file) {
    return -1;
  }
  ret = avcodec_send_frame(ctx->codec_ctx, pFrame);
  if (ret < 0) {
    return -1;
  }

  ret = avcodec_receive_packet(ctx->codec_ctx, ctx->pkt);
  if (ret < 0) {
    return -1;
  }
  if (ctx->enc_type != 0) {
    ctx->pkt->stream_index = ctx->stream->index;
    AVRational ratio = {25, 1};
    ctx->stream->time_base = av_inv_q(ratio);
    av_packet_rescale_ts(ctx->pkt, ctx->codec_ctx->time_base,
                         ctx->stream->time_base);
    ctx->pkt->pos = -1;
    ret = av_interleaved_write_frame(ctx->fmt_ctx, ctx->pkt);
    if (ret < 0) {
      return -1;
    }
    av_packet_unref(ctx->pkt); // 刷新缓存
  } else {
    ret = av_write_frame(ctx->fmt_ctx, ctx->pkt);
    if (ret < 0) {
      return -1;
    }
    av_packet_unref(ctx->pkt);
  }
  return 0;
}

int CMediaFile::GetRadarDataInSEI(const char *file, SEI_MAP &sei_map) {
  int vdo_idx = -1;
  AVCodecContext *cod_ctx = nullptr;
  AVFormatContext *fmt_ctx = nullptr;
  int ret = -1;
  do {
    // 打开H.264文件
    fmt_ctx = avformat_alloc_context();
    if (avformat_open_input(&fmt_ctx, file, nullptr, nullptr) != 0) {
      break;
    }

    // 查找视频流索引
    for (unsigned int i = 0; i < fmt_ctx->nb_streams; i++) {
      if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
          fmt_ctx->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264) {
        vdo_idx = i;
        break;
      }
    }

    if (vdo_idx == -1) {
      break;
    }

    AVCodecParameters *codecParameters = fmt_ctx->streams[vdo_idx]->codecpar;

    // 查找解码器
    const AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id);
    if (codec == nullptr) {
      break;
    }

    // 初始化解码器上下文
    cod_ctx = avcodec_alloc_context3(nullptr);
    if (avcodec_parameters_to_context(cod_ctx, codecParameters) != 0) {
      break;
    }

    if (avcodec_open2(cod_ctx, codec, nullptr) != 0) {
      break;
    }

    // 读取数据包并解码
    AVPacket packet;
    AVFrame *frame = av_frame_alloc();
    if (!frame) {
      break;
    }

    while (av_read_frame(fmt_ctx, &packet) >= 0) {
      if (packet.stream_index == vdo_idx) {
        // 解码视频帧
        int response = avcodec_send_packet(cod_ctx, &packet);
        if (response < 0) {
          break;
        }

        while (response >= 0) {
          response = avcodec_receive_frame(cod_ctx, frame);
          if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
            break;
          } else if (response < 0) {
            return -1;
          }

          for (int i = 0; i < frame->nb_side_data; ++i) {
            AVFrameSideData *side_data = frame->side_data[i];
            if (side_data->type == AV_FRAME_DATA_SEI_UNREGISTERED) {
              // LOGD("frame pts %lld", frame->pts); // 10*1000 2000*5

              int32_t ndata = side_data->size;
              const uint8_t *pdata = side_data->data;
              for (int j = 0, off = 0; j < ndata; j++, off++) {
                if (pdata[j] == 0x5a && pdata[j + 1] == 0x0c &&
                    pdata[j + 2] == 0x00) {
                  double pts = packet.pts / 10000 + (double)off * 0.2;
                  sei_map[pts] = std::string((const char *)&pdata[j], 12);
                  j += 11;
                }
              }
            }
          }
        }
      }
      ret = 0;
      av_packet_unref(&packet);
    }

    av_frame_free(&frame);
  } while (0);
  if (ret != 0) {
    return -1;
  }
  if (fmt_ctx) {
    avformat_close_input(&fmt_ctx);
    fmt_ctx = nullptr;
  }
  if (cod_ctx) {
    avcodec_free_context(&cod_ctx);
    cod_ctx = nullptr;
  }
  return sei_map.size();
}

int CMediaFile::SaveRadarDatatoFile(const char *file_name, SEI_MAP radar_data) {
  QFile file(file_name);
  if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
    return -1;
  }
  QTextStream out(&file);
  out << "PTS, Distance, Amplitude, Data\n";
  auto it = radar_data.begin();
  for (; it != radar_data.end(); it++) {
    int size = it->second.size();
    unsigned char *pdata = (unsigned char *)it->second.data();
    if (pdata[0] != 0x5a) {
      continue;
    }
    QString strData;
    for (int i = 0; i < size; i++) {
      /*转为大写，固定2位的字符串，用0占位*/
      strData.append(QString::number(pdata[i], 16).toUpper().rightJustified(2, '0'));
    }
    int len = (int)(pdata[4]) << 8 | pdata[3];
    int amp = (int)(pdata[6]) << 8 | pdata[5];
    QString hex = CGenOpt::DoubleToHexString(it->first);
    out << hex << "," << len << "," << amp << "," << strData << "\n";
  }
  file.close();
  return 0;
}

int CMediaFile::ReadRadarDataFromFile(const char *file_name,
                                      SEI_MAP& radar_data) {
  QFile file(file_name);
  uint8_t raw_data[32];
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    return -1;
  }
  QTextStream in(&file);
  while (!in.atEnd()) {
    QString line = in.readLine();
    QStringList fields = line.split(","); // 假设 CSV 文件字段由逗号分隔
    QString data = fields.at(3);
    int size = 0;
    if(data.indexOf("Data") != -1) {
      //跳过表头
      continue;
    }
    for (int i = 0; i < data.size(); i = i + 2) {
      QString number = data.mid(i, 2);
      bool bFlag = false;
      raw_data[i / 2] = number.toUInt(&bFlag, 16);
      size++;
    }
    double d_pts = CGenOpt::HexStringToDouble(fields.at(0));
    radar_data[d_pts] = std::string((const char *)&raw_data[0], size);
  }

  file.close();
  return 0;
}